ActiveX Script Key Specification Worldgroup 3.20 NT Overview: Aside from the traditional locks & keys security that Worldgroup supplies, more custom means of securing resources are usually required to fit the needs of some administrators. To provide functionality beyond that which Worldgroup currently supports, pseudo-keys were used to provide a programmatic means of determining whether or not the conditions for a lock were satisfied. The only drawback to this approach was that the administrator was either required to develop (or acquire the services of a third party to develop) the programmatic pseudo-key. This required the need to purchase a Worldgroup developer's kit, know 'C' and be fairly comfortable with the Worldgroup development platform. This can be an unnecessary expense if the administrator only needs to make a few small, but significant enhancements to the security model. Solution: Because Worldgroup is now a 32-bit application and it runs on the Windows platform, it can benefit from the enhancements and APIs available to all 32-bit Windows developers. With the advent of Internet Explorer 4.0, the capability for an application to be a scripting host is fairly trivial. In that regard, Worldgroup itself can become a scripting host for the purpose of enhancing the locks & keys security model. Since providing pseudo-keys can enhance Worldgroup, this feature could be used to instead execute a script, which then determines what the pseudo-key returns as its result. The advantages to this approach are as follows: * Only one pseudo key is required and can run a multitude of scripts. * Scripts can be written in any scripting language developed specifically for ActiveX scripting. Currently, VBScript, JavaScript, Perl and REXX scripting engines are available. * Scripts can have read-only access to common system and user variables that can be used for determining the requirements (through script) to satisfy a lock. * The administrator can assign a set of pseudo-keys to locks in the .MSG files once, then modify the scripts to accommodate for changes in functionality. * The scripts can be modified in real-time while the server is running and does not require that the Worldgroup server be stopped and restarted. I. Worldgroup Components i. MDF File ii. MSG File iii. Executable Components iv. Miscellaneous II. Pseudo-Key Anatomy i. Prefix ii. Filename III. Script Engine Information i. Scripting Engine Detection ii. Script Directives a. @LANGUAGE b. @DEFRESULT IV. Scripting Objects i. User Object ii. Server Object iii. Class Object iv. Audit Object V. Extensibility i. Script Object Registration I. Worldgroup Components i. MDF File Below are the contents of the WGAXS.MDF module definition file. Because the module has no interactive interface, it is declared as 'internal' so that it does not appear on the menu tree. --- snip here -------- snip here -------- snip here -------- snip here --- ; WGAXS.MDF Module name: WG ActiveX Scripting Developer: Galacticomm Requires: Replaces: Internal Unconditional DLLs: WGAXS MSGs: WGAXS I Need: --- snip here -------- snip here -------- snip here -------- snip here --- ii. MSG File The MSG file, WGAXS.MSG contains options used to provide a modest amount of configurable options for the administrator. Below is a list of the option names, their types and their corresponding CNF levels: Level 3 (Security Options): SCPADT - Yes/No option. If set to yes, the script module will post the name of the executed script to the audit trail. Default value: NO Level 4 (General Configuration): SCPPFX - Text option. This contains the pseudo-key prefix (maximum of 8 characters) used to determine what prefix is used for scripted keys. The default value for this option is "_SCP#". SCPPTH - Text option. This contains the (relative or absolute) path in which script files will be located. All scripts will need to reside in this chosen directory while the server is running. The default value for this option is ".\SCRIPTS". SCPEXT - Text option. This option contains the filename extension (up to 3 characters maximum) which will be used to identify Worldgroup pseudo-key scripts. The period character that normally separates the filename from the extension is not included as part of this option. The default value for this option is "WGS". SCPENG - Text option. This option contains the name of the default script engine that will be used if the script does not specifically specify which engine it would like to use. For example, if you find yourself writing all of your scripts with JScript, you can leave out the script parser directive and set this value to "JScript". Is not the name of one of the detected scripting engines, then any script without the parser directive will immediately fail and return true or false for the key based on the setting of the SCPFFAIL option. The default value for this option is "VBScript" SCPFAIL - Yes/No option. This option specifies whether a true or false result will be returned for the scripted key in the event that there is a script failure. The default value for this option is NO. A script is considered failed in one of the following cases: * A script is processed that provides a parser directive and does not have an appropriate parser that should have been detected when the Worldgroup Script Key module was initialized. * A script does not supply a parser directive and the parser name provided in CNF option SCPENG does not exist as an appropriate parser that should have been detected when the module was initialized. * An instance of the specified or desired scripting engine cannot be created. * An error occurs in the script code that is not handled by the script. Level 95 (Debugging) SCPDIAG - Yes/No option. This option is used to allow extra debugging information to be posted to the audit trail. This information can be used as an aid to determine where problems may exist during script execution. This option is set to NO by default. SCPFREC - Yes/No option. This option specifies whether or not script errors that occur should be recorded to the failure log. The log file that is used to store the information is specified by the Level 95 option SCPFLOG. This option is set to NO by default. SCPFLDT - Yes/No option. This option specifies whether or not the failure log is cleared each time the server is brought online. If the Level 95 option SCPFREC is set to NO, then the log is not deleted even if this option is set to YES. The default for this option is YES. SCPFLOG - Text option. This specifies the file name of the script error log that will be used to record script errors that occur. This file is stored in the same directory specified by the Level 4 SCPPTH option. The default value for this option is "WGSAXS.LOG". iii. Executable Components The only executable portion of the product is the WGAXS.DLL, which also contains the exported functions necessary for other third-party add-ons to register their own script-accessible objects. iv. Miscellaneous The following file is used as part of a distributed source kit: WGAXS.DEF - This file will contain the exported functions which third-party developers can use to expose their own objects as global script objects in order to extend the usefulness of the scripted key model. Borland, Visual C++ or other such developers will use this file in order to create an import library. (This is not distributed with the beta version.) II. Pseudo-Key Anatomy i. Prefix The maximum size of a pseudo-key (without the null terminator) is 15 characters. Of these, a maximum of 8 is allowed for the administrator to configure their own unique pseudo-key prefix. The remaining portion of the key will be used to designate the script file. ii. Filename The remaining portion of a pseudo-key that is not consumed by the prefix is available to specify the name of the script file. The name of the script file does not contain any path information (as this is a separate CNF option) nor does it contain the filename suffix, which is also a configurable CNF option. III. Script Engine Information i. Script Engine Detection Worldgroup uses COM facilities in order to create instances of the various scripting engines. These engines are detected by using the ICatInformation interface to inquire for objects that implement the ActiveScriptParse component category. All components that support the necessary interfaces (namely, IActiveScriptParse) are enumerated. Those components without a period ('.') in their ProgID are selected and the ProgID (for example 'VBScript', 'JScript') is used by both the Level 4 option SCPENG and the '@LANGUAGE' preprocessor directive. It is possible that there are script engine components in this category that cannot be created for scripting and may result in audit trail errors regarding the creation of said engine. Successful engine creation attempts logged in the audit trail will show which script engines are available. For each component that meets the aforementioned criteria, the following steps are performed: * An attempt to instantiate the instance of the IActiveScript object (with the enumerated GUID) is created. If this fails, then the script name is not added to the map of available script engines. * When the IActiveScript object is successfully created, it is then queried for the IActiveScriptParse interface that it holds. If this fails, then the script name is not added to the map of available script engines. * If the creation of both the IActiveScript and IActiveScriptParse objects succeeds, and a script engine of that name already exists in the map, then the engine is not added to the map (naturally) of script engines. Note: Because the ProgIDs of these components that implement this category cannot clash, this should never happen. The objects stored in the map use the script engine's ProgID (or name) to look it up in the map of instantiated script engine objects. ii. Script Directives Script directives are used to allow special script pre-processing before the ActiveX scripting engine commences execution on the script. Currently, there are only two script pre-processor directives that are supported, @LANGUAGE and @DEFRESULT. The format of a script directive is @DIRECTIVE:Argument. There may be no spaces in between any of the delimiters. Below is an example of both correct and incorrect directive statements. Correct: @LANGUAGE:JScript @DEFRESULT:False Incorrect: @ LANGUAGE: VBScript @DEFRESULT : True Because the entire script is preprocessed before the script is executed, these directives may appear on any line of the script. In fact, both directives may appear on the same line as long as they are separated by at least one space. The script directives and their arguments are case insensitive, so you may use '@Language' instead of '@LANGUAGE', etc. In order to ensure that the scripting language in use does not misconstrue the directives to be a part of the script code, the directives should be placed on a line that would normally be considered a 'comment' by the language. You may place tabs and whitespace before the script directive if you would like to make it more readable. The only restriction is that the script format described above is followed. Below is an example of different script languages and their comment blocks: VBScript: ' @LANGUAGE:VBScript ' @DEFRESULT:False or: ' @Language:VBScript @DefResult:True JScript: /* @Language:JScript @DefResult:False */ or: // @LANGUAGE:JScript // @DEFRESULT:True Below is a description of the two script directives available for Worldgroup Script Keys: @LANGUAGE:ParserName The @LANGUAGE directive alerts the pre-processor as to which ActiveX script engine parser should be used to parse the code. ParserName corresponds directly with the name of a parsing engine that has been added to the map of automatically detected parsers. The names of the available parsers are displayed in the audit trail when the Worldgroup Script Key module is initiallized. If this directive is not specified in the script, then the script engine parser specified in CNF Level 4 option SCPENG is used to execute the script. This approach may be opted if you develop your scripts in mainly one particular language and do not want to specify the parser for each script. @DEFRESULT:True | False The @DEFRESULT directive indicates the default key result set when the script fails with an error not specifically handled by the script. This includes any script errors that occur in the script itself or those that are thrown by the objects that the script may use. If this directive is not used then the CNF Level 4 option SCPFAIL is used to determine the default result returned for the key in the event of a script failure. The DEFRESULT directive will attempt to match the value following the colon (':') to "true". (This comparison is case insensitive.) Any other values are assumed to be false. Care should be used when using this directive, as specifying a default result of true can allow individuals access to the resource if no valid script engine is available to parse the given script or if the script fails. IV. Scripting Objects i. User Object The user object is used to get specific read-only information about a user account. The name of the user object exposed to the script is 'User'. The user properties are as follows: * UserID - User's handle or ID * Channel - Channel on which user is present (if applicable) * Password - Password for their account * Name - Name of the user * Birthdate - User's birthdate * Age - User's age * Sex - User's sex * Address(x) - Address lines (x is one of the following enums) * adrLine1 - Address line one (typically company name) * adrLine2 - Address line two * adrLine3 - Address line three (city, state, zip) * adrLine4 - Address line four (country) * Phone - User phone number * System - Type of computer * sysOther - other * sysIntel - IBM PC or compatible * sysMacintosh - Apple Macintosh * sysAppleNonMac - Apple, non-Macintosh PC * Language - User's current language setting * PreferenceFlags - Preference settings * AnsiFlags - ANSI settings * ScreenWidth - Screen Width * ScreenLength - Screen Length for Full Screen Editor * BreakLength - Number of lines at which output is paused * CreationDate - Account creation date * LastUsedDate - Date account was last used * UserFlags - Special user flags * AccessFlag(x) - Special user access flags (x one of the following enums) * rmtBroadcast - can send a message to all users * rmtNotify - can send a message to an individual user * rmtLogonMessage - can modify the logon message * rmtUserDetail - can see the details of another user * rmtAudit - can view the audit trail * rmtOnlineUsers - can view users online * rmtSearchUsers - can search for a user * rmtDisconnectUser - can disconnect a user * rmtSuspendUser - can suspend a user * rmtProtect - can protect a user * rmtDelete - can tag a user for deletion * rmtShutdownServer - can shutdown the server * rmtCleanup - can take the server down for cleanup * rmtSystemStatistics - can view system statistics * rmtModuleStatistics - can view module statistics * rmtDemographics - can view demographics * rmtClassStatistics - can view class statistics * rmtEmulate - can emulate another user * rmtMonitor - can monitor channels * rmtMonitorInput - can only monitor input * rmtChannel - can modify a channel * rmtTypeFile - can use the remote 'type' file command * rmtCopyFile - can use the remote 'copy' file command * rmtRenameFile - can use the remote 'rename' command * rmtDirectory - can use the remote 'dir' command * rmtMakeDir - can use the remote 'md' command * rmtRemoveDir - can use the remote 'rd' command * rmtDeleteFile - can use the remote 'del' command * rmtClassEdit - can edit classes remotely * rmtFileTransfer - can perform remote administrator file transfers * rmtAdministrator - can change the access flags of other users * EmailLimit - E-mail limit reached so far. * PermanentClass - User's permanent class * CurrentClass - Class user is currently in * SecondsOnToday - Number of seconds online today * DaysLeftInClass - Days left in this class (if applicable) * DebtForgiveDays - Days since debt was last forgiven * CreditsAvailable - Available credits (or debt if negative) * TotalFreeCredits - Total free credits * TotalPaidCredits - Total paid credits left The following method is available to provide key checking * result=HasKey(key) - See if user has a given key * result - True if user has the key, False otherwise * key - name of the key (the function protects against recursion) Note: The HasKey() method will fail if the user attempts to use a scripted key from within a script. This is to make sure that the scripting engine is not re-entered while already executing a script. This was done by design due to threading issues with Worldgroup. ii. Server Object The server object is exposed to scripts as the name 'Server'. It exposes a set of properties that indicate the current state of the server. These properties are as follows: * TotalDownloads - Total number of downloads from the server * TotalUploads - Total number of uploads to the server * TotalMessages - Total number of messages (e-mail and forums) * OpenEmail - Open e-mail messages * OpenForums - Open forum messages * HighForumNumber - Highest forum number to date * TotalMaleAccounts - Total number of male accounts * TotalFemaleAccounts - Total number of female accounts * TotalCorporateUsers - Total number of corporate users * TotalANSIUsers - Total number of ANSI users * TotalPaidCredits - Total paid credits posted to date * TotalFreeCredits - Total free credits given to date * LastZeroDate - Date of last zeroing of statistics * LastCleanupDate - Date of last cleanup iii. Class Object The class object is exposed to the script with the name 'Class'. Below are the properties for the class that the user resides in: * Name - Class name * ReturnClass(x) - Class to return to (x is one of the following enums) * swtOutOfTime - user is out of time for the day * swtIdleDays - user was idle for a number of days * swtExpired - user has been around a number of days * swtCredits - user has run out of credits * LimitPerSession - Time limit per session * LimitPerDay - Time limit per day * ExpireDays - Number of days before expiring (-1 if never) * DebtLimit - Debt limit for the class * ForgiveDays - Days until debt is forgiven * IdleDays - Days * SecondsUsed - Seconds used by this class * UserCount - Number of users in this class * Flags(x) - Special bit flags (x is one of the following enums) * clsTimeKickOff - user is kicked off when out of time * clsTimeClassChange - class is temporarily changed when user out of time * clsExpireNoCredits - class expires when user is out of credits * clsExpireDebtLimit - class expires when debt limit is reached * clsExpireHasCredits - class expires when credits are posted * clsExpireDaysPass - class expires when a number of days pass * clsExpireIdleDays - class expires when user has is idle a number of days * clsForgiveMonday - user debt is forgiven every Monday * clsForgiveMonthly - user debt is forgiven on the first of each month * clsForgiveDays - user debt is forgiven after a number of days * clsForgiveDebtLimit - user debt is forgiven when debt limit is reached * clsReportForgivenDebt - report is generated when debt is forgiven * clsCreditExempt - class is exempt from credit charges iv. Audit Object The audit object is provided to allow a rudimentary means of access to the audit trail, typically for script debugging. It basically has only one method: * Post(header,message) * header - brief header to appear in the audit trail * message - more descriptive message to post in audit trail v. Result Object The result object is used to return the final result of the script to the pseudo-key function. If the script does not set the result object, it will take on the value either specified in the DEFRESULT directive first, (if it exists in the script) or the result specified in the Level 4 option SCPFAIL. This object only has one property, which is the default property in VBScript: * Value - set to either true or false or the result of a Boolean expression V. Extensibility (For Developer's Only) In order to increase the value of the scripting model, it is anticipated that adding new or unforeseen objects can enhance security in the future. It is also anticipated that administrators may want to augment the security model with data that is provided by other baseline modules or third-party add-ons at a later date. By making an API available to external applications, said applications can register and expose their own objects to the script. Because of this feature, it is not necessary for add-ons to duplicate the efforts of the scripting host. Instead, they only need to provide the automation interface used to either manipulate the data or automate their application. Developers should be aware that the script engine is running in process with the Worldgroup Server and that user/channel servicing is blocked during the execution of a script. Therefore, it is advisable to keep any automation to a minimum and encourage script developers to create fairly terse scripts. i. Script Object Registration The following API functions are provided to allow third-party objects to be exposed to the Worldgroup Script Keys scripting host: short __stdcall axsInitializeEngine(void); Any 3rd party module that wishes to expose objects to Worldgroup's scripting host must first make sure that the scripting host engine is initialized. This is done by making a call to this function *from within the module's init__xxxx() routine. Note that all objects that are to be registered with the Worldgroup scripting host must be apartment threaded, in-process, automation-compliant COM objects. short __stdcall axsRegisterObject( char const* pszName, IDispatch* pDispatch); This function is used to register a named object with the Worldgroup scripting host. It accepts a pointer to the name of the object, as it should appear in the script. The passed IDispatch pointer must point to a valid dispinterface for an apartment threaded, in-process and automation compliant COM object. The result is non-zero if succeeded or 0 if the function fails. This function will fail if the developer attempts to register a named object that has been registered either by the Worldgroup scripting host or by another module. This function will AddRef() the IDispatch interface pointer, so you may Release() your copy if you no longer require it. short __stdcall axsRegisterObjectEx( char const* pszName, long lFlags, IDispatch* pDispatch); This function works exactly the same as axsRegisterObject(), however, it also take a set of flags that correspond to the bit-flags that may be set when adding named objects using ActiveX scripting. Information on these flags can be found in the documentation for the IActiveScript interface. (Specifically the AddNamedItem() method.) The default flags used in the axsRegisterObject() call are: long lFlags= SCRIPTITEM_ISVISIBLE|SCRIPTITEM_ISSOURCE|SCRIPTITEM_GLOBALMEMBERS; void __stdcall axsGetUserContext( char const ** ppKeyname, int* piUserNum, struct usracc** pUserAcc); Objects which are exposed to the Worldgroup scripting host are usually more useful when there is information provided about the current user whose access is being examined. Third party COM objects can internally call this function to get the following context sensitive information: * The name of the key that is being examined (the name of the pseudo-key). * The current user's number (if the user is logged on, -1 otherwise). * A pointer to the user account structure whether that individual is logged on or not. Developers should treat this information as read only and not attempt to modify the structure's contents. Methods and properties created for the COM object should be as small as possible and not do anything that is extremely time intensive. Because calling methods within a script are done in the same thread as every other add-on within Worldgroup, the less processing that needs to be done, the more responsive the server.